/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package modele;

import modele.combinaison.Combinaison;
import modele.combinaison.PetiteSuite;
import exception.CombinaisonInconnue;
import exception.ScoreAvantLancerException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import modele.combinaison.As;
import modele.combinaison.Brelan;
import modele.combinaison.Carre;
import modele.combinaison.Chance;
import modele.combinaison.Cinq;
import modele.combinaison.Deux;
import modele.combinaison.Full;
import modele.combinaison.GrandeSuite;
import modele.combinaison.Quatre;
import modele.combinaison.Six;
import modele.combinaison.Trois;
import modele.combinaison.Yams;

/**
 * Classe ModeleYams qui gère l'ensemble des données métier liées au jeu 
 * @author Florian MININ & Khémon BEH
 */
public class ModeleYams implements ModeleYamsInterface{

    /**
     * Attriuts
     */
    private List<De> listDeDes;
    private int nbLanceDesRestant = 0;
    private AbstractJoueur joueur1, joueur2;
    /*Map de combinaison qui associe pour chaque valeur de dé (1,2,3,4,5,6)
    le nombre de dé sélectionné ayant cette valeur
    Exemple: si le joueur a dans sa main deux 1 et trois 4
    la map aura les valeurs:
     1 => 2
     2 => 0
     3 => 0
     4 => 3
     5 => 0
     6 => 0
    */
    private Map<Integer, Integer> mapCombi;
    private Set<Combinaison> ensembleCombi;
    public final static int CODE_JOUEUR1 = 1;
    public final static int CODE_JOUEUR2 = 2;
    public final static String IA_SIMPLE = "iaSimple";

    /**
     * Constructeur par défaut
     * @param _joueur1
     * @param _joueur2
     */
    public ModeleYams() {

        //initialisation du Set de combi
        ensembleCombi = new LinkedHashSet<Combinaison>();
        ensembleCombi.add(new As());
        ensembleCombi.add(new Deux());
        ensembleCombi.add(new Trois());
        ensembleCombi.add(new Quatre());
        ensembleCombi.add(new Cinq());
        ensembleCombi.add(new Six());
        ensembleCombi.add(new Brelan());
        ensembleCombi.add(new Carre());
        ensembleCombi.add(new Full());
        ensembleCombi.add(new PetiteSuite());
        ensembleCombi.add(new GrandeSuite());
        ensembleCombi.add(new Yams());
        ensembleCombi.add(new Chance());

        //initialisation de la map
        mapCombi = new TreeMap<Integer, Integer>();
        mapCombi.put(1, 0);
        mapCombi.put(2, 0);
        mapCombi.put(3, 0);
        mapCombi.put(4, 0);
        mapCombi.put(5, 0);
        mapCombi.put(6, 0);
        listDeDes = new ArrayList<De>();
        for (int i = 0; i < 5; i++) {
            listDeDes.add(new De());
        }
        
        nbLanceDesRestant = 3;
    }

    public void ajouteJoueurs(AbstractJoueur _joueur1, AbstractJoueur _joueur2){
        joueur1 = _joueur1;
        joueur1.setJoue(true);
        joueur2 = _joueur2;
    }

    /**
     * Méthode qui retourne le code joueur de celui à qui c'est le tour de jouer
     * @return
     */
    public AbstractJoueur aQuiLeTour() {
        nbLanceDesRestant = 3;
        //On replace tous les dés à l'état disponible
        for (De d : listDeDes) {
            d.setDisponible(true);
        }
        if (joueur1.getJoue()) {
            joueur1.setJoue(false);
            joueur2.setJoue(true);
            return joueur2;
        } else {
            joueur2.setJoue(false);
            joueur1.setJoue(true);
            return joueur1;
        }
    }

    /**
     * Méthode qui retourne le joueur actuel
     * @return
     */
    public AbstractJoueur getJoueurActuel() {
        if (joueur1.getJoue()) {
            return joueur1;
        } else {
            return joueur2;
        }
    }

    /**
     * Méthode qui permet de jeter les dés 
     * @return
     */
    public List<Integer> jetteDes() {
        List<Integer> l = new ArrayList<Integer>();
        for (De d : listDeDes) {
            //Si le dé n'est pas sélectionné on le lance
            if (d.estDisponible()) {
                l.add(d.lancer());
            }//Sinon le dé garde sa valeur actuelle
            else {
                l.add(d.getValeur());
            }
        }
        //On décrémente le nombre restant de dés
        nbLanceDesRestant--;
        return l;
    }


    /*
     * Méthode qui permet de mettre à jour la map de combinaison et de sélectionné
     * Elle est appelée par le controleur 
     * @param i
     * @return 
     */
    public void selectDe(int i) {
        int val, nb;
        De d = listDeDes.get(i - 1);
        if (d.estDisponible()) {
            d.setDisponible(false);
            //On récupère la valeur du dé
            val = d.getValeur();
            nb = mapCombi.get(val);
            //On incrémente la valeur de 1 le nombre de dé de cette valeur
            //dans la map
            mapCombi.put(val, nb + 1);
        }
    }

    /**
     * Selectionne le de passé en paramètre
     * Elle est appelée par le l'IA
     * @param de Dé a selectionner
     */
    public void selectionneDe(De de) {
        int val, nb;
        if (de.estDisponible()) {
            de.setDisponible(false);
            //On récupère la valeur du dé
            val = de.getValeur();
            nb = mapCombi.get(val);
            //On incrémente la valeur de 1 le nombre de dé de cette valeur
            //dans la map
            mapCombi.put(val, nb + 1);
        }
    }


    /*
     * 
     * @param i
     * @return true si le dé peut être déselectionné, false sinon
     */
    public boolean deselectDe(int i) {
        int val, nb;
        De d = listDeDes.get(i - 1);
        if (d.estDisponible()) {
            return false;
        }
        d.setDisponible(true);
        val = d.getValeur();
        nb = mapCombi.get(val);
        mapCombi.put(val, nb - 1);
        return true;
    }

    /**
     * Déselectionne le dé passé en paramètre
     * @param de Dé a deselectionner
     */
    public void deSelectionneDe(De de) {
        int val, nb;
        if (!de.estDisponible()) {
            de.setDisponible(true);
            val = de.getValeur();
            nb = mapCombi.get(val);
            mapCombi.put(val, nb - 1);
        }
    }

    /**
     * Calcule combien peuvent rapporter en point chaque combinaison
     */
    public Map<String, Integer> calculScoreCombis() {
        Map<String, Integer> mapScore = new LinkedHashMap<String, Integer>();
        for (Combinaison combi : ensembleCombi) {
            combi.recupereValeurDes(mapCombi);
            mapScore.put(combi.getNomCombinaison(), combi.calculeScoreCombinaison());
        }
        return mapScore;
    }

    /**
     * Retourne vrai si le dé n'a pas été choisi, faux sinon
     * @param i
     * @return boolean
     */
    public boolean estDispo(int i) {
        De d;
        switch (i) {
            case 1:
                d = listDeDes.get(0);
                return d.estDisponible();
            case 2:
                d = listDeDes.get(1);
                return d.estDisponible();
            case 3:
                d = listDeDes.get(2);
                return d.estDisponible();
            case 4:
                d = listDeDes.get(3);
                return d.estDisponible();
            case 5:
                d = listDeDes.get(4);
                return d.estDisponible();
            default:
                return false;
        }
    }

    public Map<Integer, Integer> getMapCombinaison() {
        return mapCombi;
    }

    public int getNbLanceDesRestant() {
        return nbLanceDesRestant;
    }

    /**
     * Méthode qui met à jour le score de la combinaison sélectionné par
     * l'utilisateur.
     */
    public int enregistreScoreTour(String combinaison) throws CombinaisonInconnue, ScoreAvantLancerException {
        if(nbLanceDesRestant == 3){
            throw new ScoreAvantLancerException();
        }
        AbstractJoueur joueur = getJoueurActuel();
        int score = joueur.MAJFeuilleScore(combinaison, mapCombi);
        reinitialiser();
        return score;
    }

    private void reinitialiser() {
        for (Integer i : mapCombi.keySet()) {
            mapCombi.put(i, 0);
        }
    }

    /**
     * Retourne la valeur du dé dont le numéro est passé
     * en paramètre
     * @param numDes Numéro du dé
     * @return <tt>int</tt> correspondant a la valeur du dé.
     */
    public int getValeurDes(int numDes) {
        return listDeDes.get(numDes).getValeur();
    }

    /**
     * Retourne les dés lancés
     * @return la liste de dés.
     */
    public List<De> getDes() {
        return listDeDes;
    }

    /**
     * Méthode qui retourne le code joueur de celui qui a le plus de point, si les deux
     * joueurs sont à égalité il retourne null
     */
    public AbstractJoueur getLeader() {
        if(joueur1.getScore() > joueur2.getScore())
            return joueur1;
        else if(joueur2.getScore() > joueur1.getScore())
            return joueur2;
        else
            return null;
    }
}
